const CHARS_NUMBER = "0123456789".split("");
const CHARS_LOWER = "abcdefghijklmnopqrstuvwxyz".split("");
const CHARS_UPPER = "ABCDEFGHIJKLMNOPQRSTUVWXYZ".split("");

/**
 * 按键状态监听
 */
document.addEventListener("keydown", function (event) {
    CommonUtils.keyStatus[event.key] = true;
    CommonUtils.keyStatus[CommonUtils.keyStatus.length++] = event.key;
    let eventsSet = keyDownEventMap.get(CommonUtils.keyStatus.toString());
    if(eventsSet !== undefined){
        for (let callback of eventsSet) callback(event);
    }
});
/**
 * 按键弹起事件监听
 */
document.addEventListener("keyup", function (event) {
    let eventsSet = keyUpEventMap.get(event.key);
    if(eventsSet !== undefined){
        for (let callback of eventsSet) callback(event);
    }
    delete CommonUtils.keyStatus[event.key];
    delete CommonUtils.keyStatus[--CommonUtils.keyStatus.length];
});
/**
 * 按键事件监听
 * @type {Map<string, Set<function>>}
 */
const keyDownEventMap = new Map();
/**
 * 按键弹起事件监听
 * @type {Map<string, Set<function>>}
 */
const keyUpEventMap = new Map();

/**
 * 公共工具
 */
export default class CommonUtils {
    /**
     * 按键状态
     */
    static keyStatus = {
        length: 0,
        toString: function () {
            return Array.from(this).join(" ");
        }
    };

    /**
     * 按键事件监听
     * @param keys {string} 按键名，多个用空格分开
     * @param callback {function} 回调
     */
    static addKeyDownEventListener(keys, callback){
        let eventsSet = keyDownEventMap.get(keys);
        if(eventsSet === undefined) {
            eventsSet = new Set();
            keyDownEventMap.set(keys, eventsSet);
        }
        eventsSet.add(callback);
    }

    /**
     * 删除按钮事件监听
     * @param keys {string|function} 按键内容或回调函数
     * @param fun {function|undefined} 回调函数
     */
    static removeKeyDownEventListener(keys, fun){
        if(typeof keys === "string"){
            if(typeof fun === "function"){
                let events = keyDownEventMap.get(keys);
                if(events !== null){
                    return events.delete(fun);
                }
                return false;
            }
            return keyDownEventMap.delete(keys);
        }else if(typeof keys === "function"){
            let count = 0;
            for (let {callback} of keyDownEventMap.entries()) {
                if(callback && callback.delete(keys)) count++;
            }
            return count;
        }
        return false;
    }

    /**
     * 按键事件监听
     * @param keys {string} 按键名，不支持多按键
     * @param callback {function} 回调
     */
    static addKeyUpEventListener(keys, callback){
        let eventsSet = keyUpEventMap.get(keys);
        if(eventsSet === undefined) {
            eventsSet = new Set();
            keyUpEventMap.set(keys, eventsSet);
        }
        eventsSet.add(callback);
    }

    /**
     * 删除按钮事件监听
     * @param keys {string|function} 按键或回调
     * @param fun {function|undefined} 回调函数
     */
    static removeKeyUpEventListener(keys, fun){
        if(typeof keys === "string"){
            if(typeof fun === "function"){
                let events = keyUpEventMap.get(keys);
                if(events !== null){
                    return events.delete(fun);
                }
                return false;
            }
            return keyUpEventMap.delete(keys);
        }else if(typeof keys === "function"){
            let count = 0;
            for (let {callback} of keyUpEventMap.entries()) {
                if(callback && callback.delete(keys)) count++;
            }
            return count;
        }
        return false;
    }

    /**
     * 继承
     * @param obj1
     * @param obj2
     * @return {Object}
     */
    static extend(obj1, obj2) {
        if(typeof obj1 !== 'object' || typeof obj2 !== 'object')
            throw new TypeError("传入参数错误: obj1=" + obj1 + "\tobj2=" + obj2);
        for (let key in obj2) {
            obj1[key] = obj2[key];
        }
        return obj1;
    }

    /**
     * 拷贝对象
     * @param obj 数据对象
     * @param recursion 是否深拷贝
     */
    static clone(obj, recursion){
        if(typeof obj !== 'object')
            throw new TypeError("传入数据类型错误: obj=" + obj);
        if(obj === null) return null;
        if(obj.clone instanceof Function) return obj.clone();
        if(obj instanceof Array) return obj.concat();
        if(obj instanceof Map) return new Map(obj);
        let result = {};
        for (let key in obj) {
            let val = obj[key];
            if(recursion && typeof val === 'object'){
                val = CommonUtils.clone(val, recursion);
            }
            result[key] = val;
        }
        return result;
    }

    /**
     * 等待浏览器渲染
     * @param element {HTMLElement}
     * @return {number}
     */
    static waitBrowserRender(element){
        return element && element.offsetHeight;
    }

    /**
     * 获取元素高宽
     * @param element {HTMLElement} 元素
     */
    static getElementSize(element){
        if(element.style.display === "none" || element.classList.contains("d-none")){
            let oldPosition = element.style.position;
            let oldVisibility = element.style.visibility;
            let oldClassName = element.getAttribute("class");
            element.style.position = "absolute";
            element.style.visibility = "hidden";
            element.setAttribute("class", oldClassName + "d-block");
            let size = {width: element.offsetWidth, height: element.offsetHeight};
            element.style.position = oldPosition;
            element.style.visibility = oldVisibility;
            element.setAttribute("class", oldClassName);
            return size;
        }
        return {width: element.offsetWidth, height: element.offsetHeight};
    }

    /**
     * 判断是否是子节点
     * @param parent {string|HTMLElement} 父节点
     * @param element {HTMLElement} 子节点
     * @returns {boolean}
     */
    static isChild(parent, element){
        let p = element;
        while ((p = p.parentElement) !== null) {
            if(parent === p || p.classList.contains(parent)) return true;
        }
        return false;
    }

    /**
     * 随机字符串
     * @param length
     * @return {string}
     */
    static randomString(length){
        let result = [];
        for (let i = 0; i < length; i++) {
            let r = Math.random() * 3;
            if(r < 1){
                r = Math.floor(Math.random() * CHARS_NUMBER.length);
                result.push(CHARS_NUMBER[r]);
            }
            else if(r < 2){
                r = Math.floor(Math.random() * CHARS_LOWER.length);
                result.push(CHARS_LOWER[r]);
            }
            else{
                r = Math.floor(Math.random() * CHARS_UPPER.length);
                result.push(CHARS_UPPER[r]);
            }
        }
        return result.join("");
    }

    /**
     * 随机唯一key
     */
    static randomKey(){
        return new Date().getTime().toString(36) + CommonUtils.randomString(5);
    }

    /**
     * 日期格式化
     * @param pattern {string|Date} y表示年，M表示月，d表示日，H表示时，m表示分，s表示秒
     * @param date {Date|undefined} 时间
     * @return {string}
     */
    static formatDate(pattern, date){
        if(pattern instanceof Date){
            date = pattern;
            pattern = "y-M-d H:m:s";
        }
        let dateItems = {
            y: date.getFullYear(),
            M: date.getMonth() + 1,
            d: date.getDate(),
            H: date.getHours(),
            m: date.getMinutes(),
            s: date.getSeconds()
        };
        if(Number.isNaN(dateItems.y)) return "";

        let result = [];
        for(let i=0; i<pattern.length; i++){
            if(dateItems[pattern[i]]){
                result.push(dateItems[pattern[i]]);
            }else{
                result.push(pattern[i]);
            }
        }
        return result.join("");
    }
}
